
#import "SoundOAL.h"

@implementation SoundOAL

+ (SoundOAL *) getInstance {	
    static SoundOAL *sound;
	@synchronized (self) {
		if(!sound) {
			sound = [[SoundOAL alloc] init];
            [sound setupOpenAL];
		}
	}	
	return sound;
}

- (void) setupOpenAL {
    soundIDs = [[NSMutableArray alloc] init];
    soundIDDictionary = [[NSMutableDictionary alloc] init];
    soundBufferIDDictionary = [[NSMutableDictionary alloc] init];

	device = alcOpenDevice(NULL);

	if (device) {
		context = alcCreateContext(device, NULL);
		alcMakeContextCurrent(context);

        int maxSounds = 22; 
        for (int i = 0; i < maxSounds; i++) {
            NSUInteger soundID;
            alGenSources(1, &soundID); 
            [soundIDs addObject: [NSNumber numberWithUnsignedInt: soundID]];
        }
        
        [self logErrors: 0];
	}
}

- (void) loadSound:(NSString*) soundName 
                Hz:(NSUInteger) sampleRate {

    OSStatus status;
    AudioFileID fileID;
    NSString *path = [[NSBundle mainBundle] pathForResource: soundName ofType: nil];
	NSURL *afUrl = [NSURL fileURLWithPath: path];
	status = AudioFileOpenURL((CFURLRef) afUrl, kAudioFileReadPermission, 0, &fileID);
    [self logErrors: status];
    
    UInt64 outDataSize = 0; 
	UInt32 thePropSize = sizeof(UInt64);
	status = AudioFileGetProperty(fileID, kAudioFilePropertyAudioDataByteCount, &thePropSize, &outDataSize);
	[self logErrors: status];
    UInt32 fileSize = (UInt32) outDataSize;

    unsigned char *tempData = malloc(fileSize);
	status = AudioFileReadBytes(fileID, FALSE, 0, &fileSize, tempData);
	[self logErrors: status];
    status = AudioFileClose(fileID);
	
    NSUInteger bufferID;
	alGenBuffers(1, &bufferID);

    alBufferData(bufferID, AL_FORMAT_STEREO16, tempData, fileSize, sampleRate);
    
	[soundBufferIDDictionary setObject: [NSNumber numberWithUnsignedInt: bufferID] forKey: soundName];
    
	if(tempData) {
		free(tempData);
		tempData = NULL;
	}
    
    [self logErrors: status];
}

- (void) playSound: (NSString*) soundName {
	NSUInteger bufferID = [[soundBufferIDDictionary objectForKey: soundName] unsignedIntValue];
	NSUInteger soundID = [self findFreeSoundID];
	[soundIDDictionary setObject: [NSNumber numberWithUnsignedInt: soundID] forKey: soundName];
	        
	alSourcei(soundID, AL_BUFFER, 0); 
	alSourcei(soundID, AL_BUFFER, bufferID); 
	
    alSourcef(soundID, AL_PITCH, 1.0);
	alSourcef(soundID, AL_GAIN, 1.0);
	alSourcei(soundID, AL_LOOPING, AL_FALSE);

	alSourcePlay(soundID);
    [self logErrors: 0];
}

- (void) loopSound: (NSString*) soundName {
	NSUInteger bufferID = [[soundBufferIDDictionary objectForKey: soundName] unsignedIntValue];
	NSUInteger soundID = [self findFreeSoundID];
	[soundIDDictionary setObject: [NSNumber numberWithUnsignedInt: soundID] forKey: soundName];
    
	alSourcei(soundID, AL_BUFFER, 0); 
	alSourcei(soundID, AL_BUFFER, bufferID); 
	
    alSourcef(soundID, AL_PITCH, 1.0);
	alSourcef(soundID, AL_GAIN, 1.0);
	alSourcei(soundID, AL_LOOPING, AL_TRUE);
    
	alSourcePlay(soundID);
    [self logErrors: 0];
}

- (void) stopSound: (NSString*) soundName {
    id obj = [soundIDDictionary objectForKey: soundName]; 
    if (obj != NULL) {
        NSUInteger soundID = [obj unsignedIntValue];	
        alSourceStop(soundID);
        [self logErrors: 0];
    }
}

- (NSUInteger) findFreeSoundID {	
	for (NSNumber *aSoundID in soundIDs) {
        NSInteger idState;
		alGetSourcei([aSoundID unsignedIntValue], AL_SOURCE_STATE, &idState);
		if(idState != AL_PLAYING) return [aSoundID unsignedIntValue];
	}
	
	NSUInteger soundID = [[soundIDs objectAtIndex:0] unsignedIntegerValue];
	alSourceStop(soundID);
    [self logErrors: 0];
	return soundID;
}

- (void) logErrors: (OSStatus) status {
    ALenum err = alGetError();	
    if (err != 0) {
		NSLog(@"ERROR OpenAL: %d", err);
	}
    
    if (status != 0) {
        NSLog(@"ERROR OSStatus: %ld", (long) status);
    }
}

- (void) dealloc {
	for(NSNumber *soundID in soundIDs) {
        NSUInteger sID = [soundID unsignedIntValue];
		alDeleteSources(1, &sID);
	}
	
	NSEnumerator *enumerator = [soundBufferIDDictionary keyEnumerator];
	id key;
	while ((key = [enumerator nextObject])) {
		NSNumber *bufferID = [soundBufferIDDictionary objectForKey: key];
		NSUInteger bID = [bufferID unsignedIntValue];
		alDeleteBuffers(1, &bID);		
	}
	
    [soundIDDictionary release];
	[soundBufferIDDictionary release];
	[soundIDs release];
	
	alcMakeContextCurrent(NULL);
	alcDestroyContext(context);
	alcCloseDevice(device);
	[self logErrors: 0];
    
	[super dealloc];
}
@end
